﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using QuadRemote.Core;
using System.Xml;

namespace QuadRemote.Tabs
{
    public delegate void GraphChangedEventHandler(GraphFrame sender, int startTime, int endTime);

    public class GraphFrame : UserControl
    {
        public List<LogValueGraph> plots;

        int startTime;
        int endTime;
        float timePerPixel;
        
        int marginLeft;
        int marginTop;
        int marginBottom;
        int marginRight;

        int graphWidth;
        int graphHeight;

        bool realtime;
        Timer realtimeTimer;

        //bool quickRender = true;
        //Timer quickRenderTimer;

        public bool autoScale;
        public bool allowOffsets;
        public bool axisSameZero;
        public bool drawHorizontal;

        public float primaryMax;
        public float primaryMin;
        public float primaryScale;
        public float primaryScalePrev;
        public bool primaryDraw;
        public float primaryZeroOffset;

        public float secundaryMax;
        public float secundaryMin;
        public float secundaryScale;
        public float secundaryScalePrev;
        public float secundaryZeroOffset;
        public bool secundaryDraw;

        List<Button> plotButtons;
        private CheckBox chAutoUpdate;

        public event GraphChangedEventHandler GraphChanged;

        public GraphFrame() : base()
        {
            InitializeComponent();
            autoScale = true;
            //Size = new System.Drawing.Size(w, h);
            DoubleBuffered = true;
            BackColor = Color.Black;

            realtimeTimer = new Timer();
            realtimeTimer.Interval = 50;
            realtimeTimer.Tick += realtimeTimer_Tick;
            realtime = true;
            realtimeTimer.Start();

            plots = new List<LogValueGraph>();
            plotButtons = new List<Button>();

            primaryDraw = true;
            secundaryDraw = false;
            drawHorizontal = true;

            startTime = 0;
            endTime = 1000;

            updateMargins();

            Resize += GraphFrame_Resize;

            primaryScale = 10;
            primaryZeroOffset = graphHeight / 2;

            timePerPixel = graphWidth / ((float)endTime - (float)startTime);
            //points = new Point[graphWidth];

            MouseMove += GraphFrame_MouseMove;
            MouseDown += GraphFrame_MouseDown;
            MouseClick += GraphFrame_MouseClick;
            MouseUp += GraphFrame_MouseUp;
            MouseWheel += GraphFrame_MouseWheel;
            MouseEnter += GraphFrame_MouseEnter;
            MouseDoubleClick += GraphFrame_MouseDoubleClick;
        }

        public void updateMargins()
        {
            marginBottom = drawHorizontal ? 30 : 5;
            marginTop = 45;
            marginLeft = secundaryDraw ? 45 : 5;
            marginRight = primaryDraw ? 45 : 5;

            graphWidth = Width - marginLeft - marginRight;
            graphHeight = Height - marginTop - marginBottom;
        }

        public void loadXml(XmlNode xml)
        {
            while (plots.Count > 0)
            {
                removePlot(plots[0].container, plots[0].containerValue);
            }
            primaryDraw = xml.SelectSingleNode("primaryDraw").InnerText == "1";
            secundaryDraw = xml.SelectSingleNode("secundaryDraw").InnerText == "1";
            drawHorizontal = xml.SelectSingleNode("horizontalDraw").InnerText == "1";
            allowOffsets = xml.SelectSingleNode("allowOffsets").InnerText == "1";
            axisSameZero = xml.SelectSingleNode("axisSameZero").InnerText == "1";
            autoScale = xml.SelectSingleNode("autoScale").InnerText == "1";

            primaryMin = float.Parse(xml.SelectSingleNode("primaryMin").InnerText);
            primaryMax = float.Parse(xml.SelectSingleNode("primaryMax").InnerText);
            secundaryMin = float.Parse(xml.SelectSingleNode("secundaryMin").InnerText);
            secundaryMax = float.Parse(xml.SelectSingleNode("secundaryMax").InnerText);

            XmlNodeList ps = xml.SelectSingleNode("plots").ChildNodes;

            foreach (XmlNode p in ps)
            {
                LogValueGraph log = new LogValueGraph(p,graphWidth);
                addPlot(log);
            }
        }

        public XmlElement save(XmlDocument root)
        {
            XmlElement node = root.CreateElement("graph");
            node.AppendChild(root.CreateElement("primaryMin")).InnerText = primaryMin.ToString();
            node.AppendChild(root.CreateElement("primaryMax")).InnerText = primaryMax.ToString();
            node.AppendChild(root.CreateElement("secundaryMin")).InnerText = secundaryMin.ToString();
            node.AppendChild(root.CreateElement("secundaryMax")).InnerText = secundaryMax.ToString();
            node.AppendChild(root.CreateElement("primaryDraw")).InnerText = primaryDraw ? "1" : "0";
            node.AppendChild(root.CreateElement("secundaryDraw")).InnerText = secundaryDraw ? "1" : "0";
            node.AppendChild(root.CreateElement("horizontalDraw")).InnerText = drawHorizontal ? "1" : "0";
            node.AppendChild(root.CreateElement("allowOffsets")).InnerText = allowOffsets ? "1" : "0";
            node.AppendChild(root.CreateElement("axisSameZero")).InnerText = axisSameZero ? "1" : "0";
            node.AppendChild(root.CreateElement("autoScale")).InnerText = autoScale ? "1" : "0";
            XmlElement ps = (XmlElement)node.AppendChild(root.CreateElement("plots"));

            foreach (LogValueGraph l in plots)
            {
                ps.AppendChild(l.save(root));
            }
            return node;
        }

        void gotoRealtime_Click(object sender, EventArgs e)
        {
            setRealtime(true);
        }

        void GraphFrame_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            GraphPropertyWindow w = new GraphPropertyWindow(this);
            w.ShowDialog(this);
        }

        void GraphFrame_Resize(object sender, EventArgs e)
        {
            updateMargins();

            timePerPixel = graphWidth / ((float)endTime - (float)startTime);

            foreach (LogValueGraph g in plots)
            {
                g.resize(graphWidth);
                g.redraw(startTime, endTime, timePerPixel, hoverX, false);
            }
            Invalidate();
        }

        public void repositionButtons()
        {
            int y = 30;
            int x = 40;
            foreach (Button b in plotButtons)
            {
                b.Location = new Point(x, y);
                //y += 25;
                x += b.Width + 2;
            }
        }

        Color[] standardColors = new Color[]{ Color.Cyan, Color.Pink, Color.Lime, Color.Purple, Color.Cyan, Color.Orange, Color.Pink };
        public void addPlot(LogValueContainer container, int index)
        {
            Color color = standardColors[plots.Count % standardColors.Length];
            LogValueGraph valuegraph = new LogValueGraph(container, index, graphWidth, color);
            addPlot(valuegraph);
        }

        public void addPlot(LogValueGraph valuegraph)
        {
            plots.Add(valuegraph);

            Button b = new Button();
            b.Text = valuegraph.container.getName(valuegraph.containerValue);
            b.BackColor = valuegraph.pen.Color;
            b.ForeColor = Color.White;
            b.FlatStyle = FlatStyle.Flat;
            b.AutoSize = true;
            //b.Size = new System.Drawing.Size(90, 22);
            b.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
            b.Padding = new System.Windows.Forms.Padding(0, 0, 0, 0);
            b.Font = new Font(Tools.getPrimaryFont().FontFamily, 12);

            plotButtons.Add(b);
            Controls.Add(b);
            repositionButtons();
        }

        public void removePlot(LogValueContainer container, int index)
        {
            lock (plots)
            {
                for (int i = 0; i< plots.Count; i++)
                {
                    if (plots[i].container == container && plots[i].containerValue == index)
                    {
                        plots.RemoveAt(i);
                        Button b = plotButtons[i];
                        Controls.Remove(b);
                        plotButtons.RemoveAt(i);
                        break;
                    }
                }
            }
            repositionButtons();
        }

        /*void quickRenderTimer_Tick(object sender, EventArgs e)
        {
            quickRender = false;
            Invalidate();
            quickRenderTimer.Stop();
        }*/

        int prevCount = 0;
        void realtimeTimer_Tick(object sender, EventArgs e)
        {
            int count = 0;
            int max = 0;

            foreach (LogValueGraph g in plots)
            {
                if (g.container.Count > count) count = g.container.Count;
                if (g.container.maxTimestamp > max) max = g.container.maxTimestamp;
            }

            if (count > prevCount)
            {
                if (realtime)
                {
                    int diff = max - endTime;
                    endTime += diff;
                    startTime += diff;
                }
                prevCount = count;
                Invalidate();
            }
        }

        void GraphFrame_MouseEnter(object sender, EventArgs e)
        {
            //Focus();
        }

        public void SetTime(int begin, int end)
        {
            startTime = begin;
            endTime = end;
            timePerPixel = graphWidth / ((float)endTime - (float)startTime);

            /*quickRender = true;
            quickRenderTimer.Start();*/
            Invalidate();
        }

        void setRealtime(bool rt)
        {
            realtime = rt;
            if (realtime) realtimeTimer.Start();
            else realtimeTimer.Stop();

            chAutoUpdate.Checked = realtime;
        }

        void GraphFrame_MouseWheel(object sender, MouseEventArgs e)
        {
            int x = e.X;
            //int y = e.Y;
            startDragX = -1;

            //int tz = x + startTime;
            float tz = ((x - marginLeft) / (float)graphWidth);
            if (tz < 0) tz = 0;
            if (tz > 1) tz = 1;
            //tz += startTime;
//            if (tz < startTime) tz = startTime;
//            if (tz > endTime) tz = endTime;

            int range = endTime - startTime;

            if (e.Delta > 0) range = (int)(range * 1.1);
            else if (e.Delta < 0) range = (int)(range / 1.1);
            if (range < graphWidth / 20) range = graphWidth / 20;
            if (range > 60 * 60 * 1000) range = 60 * 60 * 1000;
            //if (range > testContainer.maxTimestamp - testContainer.minTimestamp) range = testContainer.maxTimestamp - testContainer.minTimestamp;

            int orgRange = endTime - startTime;
            if (Math.Abs(orgRange - range) < 2) range += e.Delta > 0 ? 2 : -2;
            startTime = (int)(startTime - (range - orgRange) * (tz));
            if (startTime < 0) startTime = 0;

            //if (startTime < testContainer.minTimestamp) startTime = testContainer.minTimestamp;
            endTime = startTime + range;
            //if (endTime > testContainer.maxTimestamp) { endTime = testContainer.maxTimestamp; startTime = endTime - range; if (startTime < testContainer.minTimestamp) startTime = testContainer.minTimestamp; }
            timePerPixel = graphWidth / ((float)endTime - (float)startTime);

            //Console.WriteLine("-- Start " + startTime + "  " + endTime);
            //quickRender = true;
            //quickRenderTimer.Start();
            Invalidate();

            if (GraphChanged != null)
                GraphChanged(this, startTime, endTime);
        }

        void GraphFrame_MouseUp(object sender, MouseEventArgs e)
        {
            startDragX = -1;
        }

        int startDragX = -1;
        int hoverX = -1;
        void GraphFrame_MouseDown(object sender, MouseEventArgs e)
        {
            startDragX = e.X;
        }

        void GraphFrame_MouseClick(object sender, MouseEventArgs e)
        {

        }

        void GraphFrame_MouseMove(object sender, MouseEventArgs e)
        {
            if (startDragX != -1)
            {
                hoverX = -1;
                int tz = (startDragX - e.X);

                if (realtime == true && tz < 0) setRealtime(false);
                if (realtime == true) return;
                int tz2 = (int)(tz / timePerPixel);
                Console.WriteLine(tz2 + " -- " + tz + " -- " + timePerPixel);
                if (tz2 == 0) return;
                int range = endTime - startTime;

                startTime += tz2;
                endTime += tz2;

                if (startTime < 0)
                {
                    startTime = 0;
                    endTime = range;
                }

                startDragX = e.X;

                //quickRender = true;
                //quickRenderTimer.Start();
                if (GraphChanged != null)
                    GraphChanged(this, startTime, endTime);
                Invalidate();
            }
            else
            {
                int hX = e.X - marginLeft;
                if (hX >= 0 && hX < graphWidth)
                {
                    hoverX = (int)(hX / timePerPixel);
                    hoverX += startTime;
                }
                else hoverX = -1;
                Invalidate();
            }
        }

        Brush bgBrush = new SolidBrush(Color.Black);
        Pen xAxis = new Pen(Color.White);
        Pen xAxisBig = new Pen(Color.LightGray);
        Pen xAxisSmall = new Pen(Color.FromArgb(64,64,0));
        Brush xAxisValues = new SolidBrush(Color.LightGray);
        
        Brush yAxisValues1 = new SolidBrush(Color.LightGray);
        Brush yAxisValues2 = new SolidBrush(Color.DarkGray);

        Pen yAxisBig1 = new Pen(Color.LightGray);
        Pen yAxisSmall1 = new Pen(Color.FromArgb(40,40,0));

        Pen yAxisBig2 = new Pen(Color.FromArgb(128,128,0));
        Pen yAxisSmall2 = new Pen(Color.FromArgb(64, 64, 0));

        private String getTimeString(int t)
        {
            String ss;
            int min = (t / 60000);
            int sec = (t % 60000) / 1000;
            float ms = (t % 1000);

            if (min > 0)
            {
                ss = min + "m";
                if (ms != 0)
                    ss += sec + "." + (ms < 100 ? "0" : "") + (ms < 10 ? "0" : "") + ms + "s";
                else if (sec != 0)
                    ss += sec + "s";
            }
            else
            {
                if (ms != 0) ss = sec + "." + (ms < 100 ? "0" : "") + (ms < 10 ? "0" : "") + ms + "s";
                else ss = sec + "s";
            }
            return ss;
        }
        private void drawBars(Graphics g)
        {
            g.FillRectangle(bgBrush, new Rectangle(marginLeft, marginTop, graphWidth, graphHeight));

            //xAxisSmall.DashPattern = new float[] { 2.0f, 5.0f };
            //xAxisBig.DashPattern = new float[] { 2.0f, 5.0f };
            // horizontal axis
            g.DrawLine(xAxis, marginLeft, marginTop + primaryZeroOffset, marginLeft + graphWidth, marginTop + primaryZeroOffset);

            float range = (endTime - startTime) / (float)graphWidth;

            if (drawHorizontal)
            {
                int horizontalBig;
                int horizontalSmall;
                if (range < 0.1) { horizontalBig = 25; horizontalSmall = 5; }
                else if (range < 1) { horizontalBig = 100; horizontalSmall = 25; }
                else if (range < 10) { horizontalBig = 1000; horizontalSmall = 250; }
                else if (range < 100) { horizontalBig = 10000; horizontalSmall = 2500; }
                else if (range < 1000) { horizontalBig = 60000; horizontalSmall = 15000; }
                else if (range < 5000) { horizontalBig = 60000 * 5; horizontalSmall = 60000; }
                else { horizontalBig = 60000 * 15; horizontalSmall = 60000 * 5; }
                int beginHorizontal = (startTime / horizontalSmall);
                beginHorizontal *= horizontalSmall;

                int horizontalX = (int)((beginHorizontal - startTime) * timePerPixel);
                while (horizontalX < graphWidth)
                {
                    Pen h = beginHorizontal % horizontalBig == 0 ? xAxisBig : xAxisSmall;
                    if (horizontalX >= 0)
                    {
                        g.DrawLine(h, horizontalX + marginLeft, marginTop, horizontalX + marginLeft, marginTop + graphHeight);
                        if (beginHorizontal % horizontalBig == 0)
                        {
                            String ss = getTimeString(beginHorizontal);
                            g.DrawString(ss, Tools.getPrimaryFont(), xAxisValues, horizontalX + marginLeft - ss.Length * 4, graphHeight + marginTop - 2);
                        }
                    }
                    beginHorizontal += horizontalSmall;
                    horizontalX = (int)((beginHorizontal - startTime) * timePerPixel);
                }
            }

            if (primaryDraw)
            {
                // right vertical axis
                g.DrawLine(yAxisBig1, marginLeft + graphWidth, marginTop, marginLeft + graphWidth, marginTop + graphHeight);

                float verticalSmall;
                float verticalBig;
                if (primaryScale >= 100) verticalSmall = 0.1f;
                else if (primaryScale >= 50) verticalSmall = 0.2f;
                else if (primaryScale >= 10) verticalSmall = 1f;
                else if (primaryScale >= 5) verticalSmall = 2f;
                else if (primaryScale >= 1) verticalSmall = 10f;
                else if (primaryScale >= 0.5) verticalSmall = 20f;
                else if (primaryScale >= 0.1) verticalSmall = 100f;
                else if (primaryScale >= 0.05) verticalSmall = 200f;
                else if (primaryScale >= 0.02) verticalSmall = 500f;
                else verticalSmall = 1000f;
                verticalSmall *= 2.5f;
                verticalBig = 5;


                int limit = 1000;
                g.DrawString(verticalSmall + "  -- " + primaryScale, Tools.getPrimaryFont(), new SolidBrush(Color.White), 10, 10);
                float i = verticalSmall;
                int j = 1;
                int y = (int)(marginTop + primaryZeroOffset - i * primaryScale);

                while (y > marginTop && limit-- > 0)
                {
                    if (!secundaryDraw || (j % verticalBig == 0))
                        g.DrawLine(j % verticalBig == 0 ? yAxisBig1 : yAxisSmall1, marginLeft, y, marginLeft + graphWidth, y);

                    if (j % verticalBig == 0)
                    {
                        g.DrawString(i + "", Tools.getPrimaryFont(), yAxisValues1, marginLeft + graphWidth, y);
                    }

                    j++;
                    i = verticalSmall * j;
                    y = (int)(marginTop + primaryZeroOffset - i * primaryScale);
                }

                i = verticalSmall;
                j = 0;
                y = (int)(marginTop + primaryZeroOffset + i * primaryScale);
                while (y < marginTop + graphHeight && limit-- > 0)
                {
                    if (!secundaryDraw || (j % verticalBig == 0))
                        g.DrawLine(j % verticalBig == 0 ? yAxisBig1 : yAxisSmall1, marginLeft, y, marginLeft + graphWidth, y);

                    if (j % verticalBig == 0)
                    {
                        g.DrawString(-i + "", Tools.getPrimaryFont(), yAxisValues1, marginLeft + graphWidth, y);
                    }

                    j++;
                    i = verticalSmall * j;
                    y = (int)(marginTop + primaryZeroOffset + i * primaryScale);
                }
            }

            if (secundaryDraw)
            {
                // right vertical axis
                g.DrawLine(yAxisBig2, marginLeft, marginTop, marginLeft , marginTop + graphHeight);

                float verticalSmall;
                float verticalBig;
                if (secundaryScale >= 100) verticalSmall = 0.1f;
                else if (secundaryScale >= 50) verticalSmall = 0.2f;
                else if (secundaryScale >= 10) verticalSmall = 1f;
                else if (secundaryScale >= 5) verticalSmall = 2f;
                else if (secundaryScale >= 1) verticalSmall = 10f;
                else if (secundaryScale >= 0.2) verticalSmall = 20f;
                else verticalSmall = 100f;
                verticalBig = 5;


                //g.DrawString(verticalSmall + "  -- " + scale1, Tools.getPrimaryFont(), new SolidBrush(Color.White), 10, 10);
                float i = verticalSmall;
                int j = 1;
                int y = (int)(marginTop + primaryZeroOffset - i * secundaryScale);
                while (y > marginTop)
                {
                    if (!primaryDraw || (j % verticalBig == 0))
                        g.DrawLine(j % verticalBig == 0 ? yAxisBig2 : yAxisSmall2, marginLeft, y, marginLeft + graphWidth, y);

                    if (j % verticalBig == 0)
                    {
                        g.DrawString(i + "", Tools.getPrimaryFont(), yAxisValues2, marginLeft, y);
                    }

                    j++;
                    i = verticalSmall * j;
                    y = (int)(marginTop + primaryZeroOffset - i * secundaryScale);
                }

                i = verticalSmall;
                j = 1;
                y = (int)(marginTop + primaryZeroOffset + i * secundaryScale);
                while (y < marginTop + graphHeight)
                {
                    if (!primaryDraw || (j % verticalBig == 0))
                        g.DrawLine(j % verticalBig == 0 ? yAxisBig2 : yAxisSmall2, marginLeft, y, marginLeft + graphWidth, y);

                    if (j % verticalBig == 0)
                    {
                        g.DrawString(-i + "", Tools.getPrimaryFont(), yAxisValues2, marginLeft, y);
                    }

                    j++;
                    i = verticalSmall * j;
                    y = (int)(marginTop + primaryZeroOffset + i * secundaryScale);
                }
            }
            g.DrawRectangle(xAxis, marginLeft, marginTop, graphWidth, graphHeight);
        }
        
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DateTime t = DateTime.Now;

            Graphics g = e.Graphics;
            //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            float minValue1 = float.MaxValue;
            float maxValue1 = float.MinValue;
            float minValue2 = float.MaxValue;
            float maxValue2 = float.MinValue;

            foreach (LogValueGraph graph in plots)
            {
                graph.redraw(startTime, endTime, timePerPixel, hoverX, true);// !quickRender);

                if (graph.axis)
                {
                    if (graph.minInPoints < minValue2) minValue2 = graph.minInPoints;
                    if (graph.maxInPoints > maxValue2) maxValue2 = graph.maxInPoints;
                }
                else
                {
                    if (graph.minInPoints < minValue1) minValue1 = graph.minInPoints;
                    if (graph.maxInPoints > maxValue1) maxValue1 = graph.maxInPoints;
                }
            }
            //Console.Write((quickRender ? 1 : 0) + " REDRAW: " + (DateTime.Now - t).TotalMilliseconds);

            if (autoScale)
            {

                minValue1 *= (minValue1 > 0) ? 0.90f : 1.1f;
                minValue2 *= (minValue2 > 0) ? 0.90f : 1.1f;
                maxValue1 *= (maxValue1 < 0) ? 0.90f : 1.1f;
                maxValue2 *= (maxValue2 < 0) ? 0.90f : 1.1f;
                

                if (!allowOffsets)
                {
                    if (minValue1 > 0) minValue1 = 0;
                    if (maxValue1 < 0) maxValue1 = 0;
                    if (minValue2 > 0) minValue2 = 0;
                    if (maxValue2 < 0) maxValue2 = 0;
                }
                
                //if (!axisSameZero)
                {
                    if (maxValue1 != minValue1)
                        primaryScale = graphHeight / (maxValue1 - minValue1);
                    else
                        primaryScale = 1;

                    if (maxValue2 != minValue2)
                        secundaryScale = graphHeight / (maxValue2 - minValue2);
                    else
                        secundaryScale = 1;

                }

                if (primaryScale > primaryScalePrev && primaryScalePrev != 0)
                {
                    primaryScale = primaryScalePrev * 1.01f;
                }
                primaryZeroOffset = primaryScale * (maxValue1);
                if (secundaryScale > secundaryScalePrev && secundaryScalePrev != 0)
                {
                    secundaryScale = secundaryScalePrev * 1.01f;
                }
                secundaryZeroOffset = secundaryScale * maxValue2;

                /*if (axisSameZero)
                {
                    primaryZeroOffset = primaryZeroOffset / 2 + secundaryZeroOffset / 2;
                    secundaryZeroOffset = primaryZeroOffset;

                    float pixelTop = primaryZeroOffset;
                    float pixelBottom = graphHeight - primaryZeroOffset;

                    if (maxValue1 > 0 && minValue1 > 0)
                    {
                        primaryScale = pixelTop / maxValue1;
                    }
                    else if (minValue1 < 0 && maxValue1 < 0)
                    {
                        primaryScale = pixelBottom / -minValue1;
                    }
                    else
                    {
                        if (pixelTop / maxValue1 > pixelBottom / -minValue1)
                            primaryScale = pixelTop / maxValue1;
                        else
                            primaryScale = pixelBottom / -minValue1;
                    }

                    if (maxValue2 > 0 && minValue2 > 0)
                    {
                        secundaryScale = pixelTop / maxValue2;
                    }
                    else if (minValue2 < 0 && maxValue2 < 0)
                    {
                        secundaryScale = pixelBottom / -minValue2;
                    }
                    else
                    {
                        if (pixelTop / maxValue2 > pixelBottom / -minValue2)
                            secundaryScale = pixelTop / maxValue2;
                        else
                            secundaryScale = pixelBottom / -minValue2;
                    }
                }*/
            }
            else
            {
                primaryScale = graphHeight / (primaryMax - primaryMin);
                primaryZeroOffset = primaryMax * primaryScale;
                secundaryScale = graphHeight / (secundaryMax - secundaryMin);
                secundaryZeroOffset = secundaryMax * secundaryScale;
            }

            primaryScalePrev = primaryScale;
            secundaryScalePrev = secundaryScale;
            //Console.Write(" SCALE: " + (DateTime.Now - t).TotalMilliseconds);

            drawBars(g);
            //Console.Write(" BARS: " + (DateTime.Now - t).TotalMilliseconds);

            int yy = 10;
            foreach (LogValueGraph graph in plots)
            {
                g.SetClip(new Rectangle(marginLeft, marginTop, graphWidth + 1, graphHeight));
                g.TranslateTransform(marginLeft, marginTop + primaryZeroOffset);
                g.ScaleTransform(1, graph.axis ? -secundaryScale : -primaryScale);
                g.DrawLines(new Pen(graph.pen.Brush,graph.pen.Width ), graph.points);
                g.ResetTransform();
                if (hoverX != -1)
                {
                    float f = (graph.hoverValue.X - startTime) * timePerPixel;
                    g.DrawEllipse(graph.pen, f - 5 + marginLeft, -graph.hoverValue.Y * (graph.axis ? secundaryScale : primaryScale) - 5 + marginTop + primaryZeroOffset, 10, 10);
                    g.DrawString(getTimeString((int)graph.hoverValue.X) + " = " + graph.hoverValue.Y + "", Tools.getPrimaryFont(), new SolidBrush(graph.pen.Color), f + marginLeft, -graph.hoverValue.Y * (graph.axis ? secundaryScale : primaryScale) + marginTop + primaryZeroOffset);
                }
                //g.DrawString(graph.benchCounter + " iterations", Tools.getPrimaryFont(), new SolidBrush(graph.pen.Color), 100, yy += 20);
            }

            //Console.WriteLine(" TOTAL:  " + (DateTime.Now - t).TotalMilliseconds);
        }

        private void InitializeComponent()
        {
            this.chAutoUpdate = new System.Windows.Forms.CheckBox();
            this.SuspendLayout();
            // 
            // chAutoUpdate
            // 
            this.chAutoUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.chAutoUpdate.AutoSize = true;
            this.chAutoUpdate.ForeColor = System.Drawing.Color.White;
            this.chAutoUpdate.Location = new System.Drawing.Point(552, 4);
            this.chAutoUpdate.Name = "chAutoUpdate";
            this.chAutoUpdate.Size = new System.Drawing.Size(85, 17);
            this.chAutoUpdate.TabIndex = 0;
            this.chAutoUpdate.Text = "Scroll to end";
            this.chAutoUpdate.UseVisualStyleBackColor = true;
            this.chAutoUpdate.CheckedChanged += chAutoUpdate_CheckedChanged;
            // 
            // GraphFrame
            // 
            this.BackColor = System.Drawing.Color.Black;
            this.Controls.Add(this.chAutoUpdate);
            this.Name = "GraphFrame";
            this.Size = new System.Drawing.Size(640, 451);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        void chAutoUpdate_CheckedChanged(object sender, EventArgs e)
        {
            setRealtime(chAutoUpdate.Checked);
        }
    }
}
